package io.github.rascaler.dcenter.exception;

import io.github.rascaler.dcenter.pojo.OuterResult;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NestedExceptionUtils;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@ControllerAdvice
public class GlobalWebExceptionHandlerBean implements Ordered {

	private final static Logger logger = LoggerFactory.getLogger(GlobalWebExceptionHandlerBean.class);



	/**
	 * 参数绑定错误
	 *
	 * @param e
	 * @return
	 */
	@ExceptionHandler(BindException.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public OuterResult handleBindException(BindException e, HttpServletRequest request) {
		logger.error("[{}]request params bind error: {}", request.getRequestURI(), e.getMessage());
		List<String> errors = new ArrayList<>();
		for (ObjectError objectError : e.getAllErrors()) {
			if (objectError.contains(XssValidException.class)) {
				XssValidException exception = objectError.unwrap(XssValidException.class);
				errors.add(exception.getMessage());
			} else {
				errors.add(objectError.getDefaultMessage());
			}
		}
		OuterResult outerResult = OuterResult.fail(StringUtils.join(errors, "，"));
		return outerResult;
	}

	/**
	 * 参数绑定错误
	 * @param e
	 * @return
	 */
	@ExceptionHandler(MethodArgumentNotValidException.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public OuterResult handleMethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest req) {
		logger.error("[{}]request params bind error: {}", req.getRequestURI(), e.getMessage());
		List<String> errors = new ArrayList<String>();
		for (ObjectError objectError : e.getBindingResult().getAllErrors()) {
			errors.add(objectError.getDefaultMessage());
		}
		OuterResult outerResult = OuterResult.fail(StringUtils.join(errors, "，"));
		return outerResult;
	}

	/**
	 * 缺少参数
	 *
	 * @param req
	 * @param e
	 * @return
	 */
	@ExceptionHandler(MissingServletRequestParameterException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public OuterResult handleMissingServletRequestParameterException(HttpServletRequest req, MissingServletRequestParameterException e) {
		logger.error("[{}]miss params: {}", req.getRequestURI(), e.getMessage());
		OuterResult outerResult = OuterResult.fail("miss params");
		return outerResult;
	}

	/**
	 * 参数类型不匹配
	 * @param req
	 * @param e
	 * @return
	 */
	@ExceptionHandler(MethodArgumentTypeMismatchException.class)
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public OuterResult handleMethodArgumentTypeMismatchExceptionException(HttpServletRequest req, MethodArgumentTypeMismatchException e) {
		logger.error("[{}]Argument Type Mismatch: {}", req.getRequestURI(), e.getMessage());
		OuterResult outerResult = OuterResult.fail("Argument Type Mismatch");
		return outerResult;
	}

	/**
	 * http消息无法解析
	 * @param req
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = HttpMessageNotReadableException.class )
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public OuterResult handleHttpMessageNotReadableException(HttpServletRequest req, HttpMessageNotReadableException e) {
		//e.getRootCause() instanceof XssValidException
		Throwable rootCause = NestedExceptionUtils.getRootCause(e);
		if (rootCause instanceof XssValidException) {
			// xss 的异常
			logger.error("Http [{}] request xssException: {}", req.getRequestURI(), e.getMessage());
			return OuterResult.fail(rootCause.getMessage());
		} else {
			logger.error("Http [{}] Message Not Readable: {}", req.getRequestURI(), e.getMessage());
			return OuterResult.fail("Http Message Not Readable");
		}
	}



	@ExceptionHandler(value = XssValidException.class )
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public OuterResult handleXssValidException(HttpServletRequest request, XssValidException e ) {
		// xss 的异常
		logger.error("Http [{}] request xss拦截成功: {}", request.getRequestURI(), e.getMessage());
		return OuterResult.fail(e.getMessage());
	}




	/**
	 * http 媒体类型不支持
	 * @param request
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = HttpMediaTypeNotSupportedException.class )
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ResponseBody
	public OuterResult handleHttpMediaTypeNotSupportedException(HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
		logger.error("[{}] Http Media Type Not Supported: {}", request.getRequestURI(), e.getMessage());
		OuterResult outerResult = OuterResult.fail("Http Media Type Not Supported");
		return outerResult;
	}

	/**
	 * HTTP方法不支持
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class })
	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public OuterResult handleMethodException(Exception ex, HttpServletRequest request) {
		logger.error("[{}]:[{}]-HTTP method不被支持", request.getRequestURI(), request.getMethod());
		OuterResult outerResult = OuterResult.fail("HTTP method不被支持，请联系管理员");
		return outerResult;
	}


	/**
	 * description: 业务异常
	 * @param servletRequest
	 * @param bizException
	 * @return com.yunbao.immune.framework.model.pojo.OuterResult
	 * @author ZENG.XIAO.YAN
	 * @time 2021-10-21 14:22
	 */
	@ExceptionHandler(BizException.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public OuterResult handleBizException(HttpServletRequest servletRequest, BizException bizException) {
		OuterResult outerResult;
		logger.error("业务出错[{}] ecode: {}，msg:{}，异常栈:{}",
				servletRequest.getRequestURI(),
				bizException.getEcode(),
				bizException.getMsg(), ExceptionUtils.getStackTrace(bizException));
		outerResult = new OuterResult(bizException.getEcode(), bizException.getMsg());
		outerResult.setData(bizException.getData());
		return outerResult;
	}

	@ExceptionHandler(Throwable.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	public OuterResult handleThrowAble(HttpServletRequest servletRequest, Throwable ex) {
		OuterResult outerResult;
		if (ex instanceof IllegalArgumentException) {
			logger.error("访问出错(" + servletRequest.getRequestURI() + ")", ex);
			outerResult = OuterResult.fail("参数异常，请检查您输入的内容");
			return outerResult;
		}
		logger.error("访问出错(" + servletRequest.getRequestURI() + ")系统异常", ex);
		outerResult = OuterResult.fail("未知的异常");
		return outerResult;
	}

	@ExceptionHandler(ConstraintViolationException.class)
	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	public OuterResult handleConstraintViolationException(ConstraintViolationException e, HttpServletRequest request) {
		logger.error("[{}]request params bind error:{} ", request.getRequestURI(), e.getMessage());
		List<String> errors = new ArrayList<String>();
		Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
		constraintViolations.forEach(x -> errors.add(x.getMessage()));
		OuterResult outerResult = OuterResult.fail(StringUtils.join(errors, "，"));
		return outerResult;
	}

//	@ExceptionHandler(IdempotentException.class)
//	@ResponseBody
//	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
//	public OuterResult handleIdempotentException(IdempotentException e, HttpServletRequest request) {
//		logger.error("[{}]request 重复提交!", request.getRequestURI());
//		OuterResult outerResult = OuterResult.fail(e.getMessage());
//		return outerResult;
//	}
//
//	@ExceptionHandler(EncryptException.class)
//	@ResponseBody
//	@ResponseStatus(HttpStatus.BAD_REQUEST)
//	public OuterResult handleSecurityException(EncryptException e, HttpServletRequest request) {
//		logger.error("[{}]签名或加解密失败", request.getRequestURI());
//		OuterResult outerResult = OuterResult.fail(e.getMessage());
//		return outerResult;
//	}





	@Override
	public int getOrder() {
		// 加个500 给别人覆盖的机会
		return Ordered.HIGHEST_PRECEDENCE + 500;
	}
}
